/*
 *  string.c
 *
 *  Created on: 2010-5-12
 *      Author: liuyond
 *    Abstract: this file will implement the simulation of the following instruction
 *    			MOVSB, MOVSW, CMPSB, CMPSW, SCASB, SCASW, STOSB, STOSW, LODSB, LODSW
 */
#include "cpu.h"

void MOVSB(cpu_t *cpu, instruction_t *i)
{
	// move byte from [DS:SI] to [ES:DI]
	int temp = cpu_get_mem_byte(cpu, cpu_get_seg(cpu, DS), cpu_get_reg(cpu, SI));
	cpu_set_mem_byte(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI), temp);
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)-sizeof(byte));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(byte));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)+sizeof(byte));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(byte));
	}
}

void MOVSW(cpu_t *cpu, instruction_t *i)
{
	// move word from [DS:SI] to [ES:DI]
	int temp = cpu_get_mem_word(cpu, cpu_get_seg(cpu, DS), cpu_get_reg(cpu, SI));
	cpu_set_mem_word(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI), temp);
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)-sizeof(word));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(word));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)+sizeof(word));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(word));
	}
}

void STOSB(cpu_t *cpu, instruction_t *i)
{
	// move from AL to [ES:DI]
	int temp = cpu_get_regb(cpu, AL);
	cpu_set_mem_byte(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI), temp);
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(byte));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(byte));
	}
}

void STOSW(cpu_t *cpu, instruction_t *i)
{
	// move from AL to [ES:DI]
	int temp = cpu_get_reg(cpu, AX);
	cpu_set_mem_word(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI), temp);
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(word));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(word));
	}
}

void LODSB(cpu_t *cpu, instruction_t *i)
{
	// move from [DS:SI] to AL
	int temp = cpu_get_mem_byte(cpu, cpu_get_seg(cpu, DS), cpu_get_reg(cpu, SI));
	cpu_set_regb(cpu, AL, temp);
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)-sizeof(byte));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)+sizeof(byte));
	}
}

void LODSW(cpu_t *cpu, instruction_t *i)
{
	// move from [DS:SI] to AX
	int temp = cpu_get_mem_word(cpu, cpu_get_seg(cpu, DS), cpu_get_reg(cpu, SI));
	cpu_set_reg(cpu, AX, temp);
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)-sizeof(word));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)+sizeof(word));
	}
}

void CMPSB(cpu_t *cpu, instruction_t *i)
{
	// [DS:SI] - [ES:DI], the result will affect flags register
	byte src, dst, ret;

	src = cpu_get_mem_byte(cpu, cpu_get_seg(cpu, DS), cpu_get_reg(cpu, SI));
	dst = cpu_get_mem_byte(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI));
	ret = src - dst;
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)-sizeof(byte));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(byte));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)+sizeof(byte));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(byte));
	}
	cpu_set_flags(cpu, CMP_BYTE, src, dst, ret);
}

void CMPSW(cpu_t *cpu, instruction_t *i)
{
	// [DS:SI] - [ES:DI], the result will affect flags register
	word src, dst, ret;

	src = cpu_get_mem_word(cpu, cpu_get_seg(cpu, DS), cpu_get_reg(cpu, SI));
	dst = cpu_get_mem_word(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI));
	ret = src - dst;
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)-sizeof(word));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(word));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, SI, cpu_get_reg(cpu, SI)+sizeof(word));
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(word));
	}
	cpu_set_flags(cpu, CMP_WORD, src, dst, ret);
}

void SCASB(cpu_t *cpu, instruction_t *i)
{
	// AL - [ES:DI], the result will affect flags register
	byte src, dst, ret;

	src = cpu_get_regb(cpu, AL);
	dst = cpu_get_mem_byte(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI));
	ret = src - dst;
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(byte));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(byte));
	}
	cpu_set_flags(cpu, CMP_BYTE, src, dst, ret);
}

void SCASW(cpu_t *cpu, instruction_t *i)
{
	// AX - [ES:DI], the result will affect flags register
	word src, dst, ret;

	src = cpu_get_reg(cpu, AX);
	dst = cpu_get_mem_word(cpu, cpu_get_seg(cpu, ES), cpu_get_reg(cpu, DI));
	ret = src - dst;
	if (cpu_get_flag(cpu, DF))
	{
		// DF = 1, decrease address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)-sizeof(word));
	}
	else
	{
		// DF = 0, increase address
		cpu_set_reg(cpu, DI, cpu_get_reg(cpu, DI)+sizeof(word));
	}
	cpu_set_flags(cpu, CMP_WORD, src, dst, ret);
}
